/************************************************************************
 * @file PluginActivator.cpp
 * @author Ian Chen
 * @version 1.0
 *
 * @section LICENSE
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 * General Public License for more details at
 * http://www.gnu.org/licenses/lgpl.html
 *
 ************************************************************************/
#include <plugin/plugin/PluginActivator.h>

#include <plugin/plugin/AbstractPlugin.h>
#include <plugin/util/Framework.h>
#include <plugin/plugin/PluginDescription.h>
#include <plugin/plugin/PluginFactory.h>
#include <plugin/service/ServiceRegistry.h>
#include <plugin/util/PluginDescriptionManager.h>
#include <plugin/util/StatusEventListener.h>

namespace antu
{
namespace plugin
{
PluginActivator::PluginActivator()
    : m_pPluginContext(Framework::Instance.context())
{
    Q_ASSERT(m_pPluginContext != nullptr);
    this->m_pServiceRegistry = new ServiceRegistry;
}

PluginActivator::PluginActivator(PluginContext* pContext)
    : m_pPluginContext(pContext)
{
    Q_ASSERT(pContext != nullptr);
    this->m_pServiceRegistry = new ServiceRegistry;
}

PluginActivator::~PluginActivator()
{
    this->m_oPlugins.clear();
    this->m_oSortedPlugins.clear();
}

void PluginActivator::init(QObject *pParent, StatusEventListener *pListener)
{
    vector<PluginDescriptionSharedPtr> descriptions;
    descriptions = Framework::Instance.descriptionManager()->findAll();

    size_t size = descriptions.size();

    if (size == 0)
        return;

    double progressStep = 100 / 3 / descriptions.size();
    double progress = 0;
    // create all plugins
    for (size_t i = 0; i < size; i++)
    {
        PluginDescriptionSharedPtr description = descriptions[i];
        AbstractPlugin* pPlugin = PluginFactory::Factory.createPlugin(description->code(),
                                            description,
                                            this->m_pServiceRegistry,
                                            pParent);
        this->m_oPlugins.push_back(pPlugin);

        this->m_oSortedPlugins.insert(pair<QString, AbstractPlugin*>(pPlugin->code(), pPlugin));
    }

    size = this->m_oPlugins.size();
    for (size_t i = 0; i < size; i++)
    {
        AbstractPlugin* pPlugin = this->m_oPlugins[i];
        pPlugin->initialize(this->m_pPluginContext);

        progress += progressStep;
        if (pListener != nullptr)
            pListener->setStatusText(QObject::tr("Loading plugins..."), (int)progress);
    }
}

void PluginActivator::init(const QStringList &plugins, QObject* pParent, StatusEventListener *pListener)
{
    vector<PluginDescriptionSharedPtr> descriptions;
    descriptions = Framework::Instance.descriptionManager()->findAllNeededDescriptions(plugins);

    size_t size = descriptions.size();

    if (size == 0)
        return;

    double progressStep = 100 / 3 / descriptions.size();
    double progress = 0;
    // create all plugins
    for (size_t i = 0; i < size; i++)
    {
        PluginDescriptionSharedPtr description = descriptions[i];
        AbstractPlugin* pPlugin = PluginFactory::Factory.createPlugin(description->code(),
                                            description,
                                            this->m_pServiceRegistry,
                                            pParent);
        this->m_oPlugins.push_back(pPlugin);

        this->m_oSortedPlugins.insert(pair<QString, AbstractPlugin*>(pPlugin->code(), pPlugin));
    }

    size = this->m_oPlugins.size();
    for (size_t i = 0; i < size; i++)
    {
        AbstractPlugin* pPlugin = this->m_oPlugins[i];
        pPlugin->initialize(this->m_pPluginContext);

        progress += progressStep;
        if (pListener != nullptr)
            pListener->setStatusText(QObject::tr("Loading plugins..."), (int)progress);
    }
}

void PluginActivator::start(StatusEventListener *pListener, bool /*isRootActivator = false*/)
{
    size_t size = this->m_oPlugins.size();
    if (size == 0)
        return;

    double progressStep = 100 / 3 / size;
    double progress = 100 / 3;
    for (size_t i = 0; i < size; i++)
    {
        AbstractPlugin* pPlugin = this->m_oPlugins[i];
        pPlugin->start();

        progress += progressStep;
        if (pListener != nullptr)
            pListener->setStatusText(QObject::tr("Loading plugins..."), (int)progress);
    }
}

void PluginActivator::finishStartup(StatusEventListener *pListener)
{
    size_t size = this->m_oPlugins.size();
    if (size == 0)
        return;

    double progressStep = 100 / 3 / size;
    double progress = 200 / 3;
    for (size_t i = 0; i < size; i++)
    {
        AbstractPlugin* pPlugin = this->m_oPlugins[i];
        pPlugin->finishStartup();

        progress += progressStep;
        if (pListener != nullptr)
            pListener->setStatusText(QObject::tr("Loading plugins..."), (int)progress);
    }
}

void PluginActivator::stop(StatusEventListener *pListener)
{
    if (this->m_oPlugins.size() == 0)
        return;

    double progressStep = 100 / this->m_oPlugins.size();
    double progress = 0;
    for (int i = this->m_oPlugins.size() - 1; i >= 0; i--)
    {
        AbstractPlugin* pPlugin = this->m_oPlugins[i];
        pPlugin->stop();

        progress += progressStep;
        if (pListener != nullptr)
            pListener->setStatusText(QObject::tr("Loading plugins..."), (int)progress);
    }
}

AbstractCommand::CommandReturnType PluginActivator::execute(const QString &commandLine, CommandParameters &outputs)
{
    int index = commandLine.indexOf(' ');
    if (index >= 0)
    {
        QString pluginCode = commandLine.mid(0, index).trimmed();
        QString line = commandLine.mid(index).trimmed();

        map<QString, AbstractPlugin*>::iterator iter = this->m_oSortedPlugins.find(pluginCode);
        if (iter == this->m_oSortedPlugins.end())
        {
            return AbstractCommand::PluginNotFound;
        }
        else
        {
            return (iter->second)->execute(line, outputs);
        }
    }
    else
    {
        return AbstractCommand::CommandLineUnrecognized;
    }
}

AbstractCommand::CommandReturnType PluginActivator::execute(const QString &pluginCode, const QString &serviceName, const QString &commandName, const CommandParameters &inputs, CommandParameters &outputs)
{
    map<QString, AbstractPlugin*>::iterator iter = this->m_oSortedPlugins.find(pluginCode);
    if (iter == this->m_oSortedPlugins.end())
    {
        return AbstractCommand::PluginNotFound;
    }
    else
    {
        return (iter->second)->execute(serviceName, commandName, inputs, outputs);
    }
}

}
}

